/* 
 * File:   main.c
 * Author: roque
 *
 * Created on 17 de mayo de 2012, 23:13
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <pwd.h>
#include <syslog.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <setjmp.h>
#include <limits.h>
#include "simpleBash.h"
#define BUFLEN 256
#define BUFSZ PIPE_BUF

typedef struct deamon_configuration{
    int num_puerto, 
        max_hilos, 
        timeout,
        backlog;
    char path_log[30], 
         logfile[30];
}deamon_conf;
deamon_conf dc;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int cant_hilos,
        bandera;
jmp_buf val;
void interface_telnet(int *);
void telnet(int *);
void signal_handler(int);
void reload_handler(int);
void open_fileconf(const char *);
int comparar_comando(char *, char *, int);
int buscar_comando(char *, int);
char *array_command[] = {"cd", "ls", "pwd", "view", "copy", "move", 
                        "delete", "mkdir", "rmdir", "close", "help"};
char *descripcion_command[] = {"Cambia el directorio",
                                "Lista archivos y directorios",
                                "Muestra directorio actual",
                                "Visualiza el contenido de un archivo",
                                "Copia un archivo a una direccion especificada",
                                "Mueve un archivo a una direccion especificada",
                                "Borra un archivo espeficado",
                                "Crear directorio",
                                "Borrar directorio",
                                "Cerrar conexion",
                                "Muestra esta ayuda"};

int main (int argc, char *argv[]) {
    pid_t pid, sid;
    time_t timebuf;
    int fd, len;
    int rcfd;
    if (argc != 2)
    	printf ("Ingrese el path del archivo de configuracion\n");

    /* 1ro. Creamos un hijo y el padre sale */
    pid=fork();
    if (pid<0) {
        syslog(LOG_ERR, "%s\n", "fork");
        exit(EXIT_FAILURE);
    }
    if (pid>0) {
        /* En el padre, liberamos */
        exit(EXIT_SUCCESS);
    }

    /* En el hijo ... */
    /* Se abre el archivo de sistema */
    openlog("prueba", LOG_PID, LOG_LOCAL0);

    /* Se lee el archivo de configuración */
    
    

    /* 2do. Iniciamos una nueva sesión */
    if ((sid=setsid())<0) {
        syslog(LOG_ERR, "%s\n", "setsid");
        exit(EXIT_FAILURE);
    }

    /* 3ro. Hacemos de / el directorio actual */
    if ((chdir("/"))<0) {
        syslog(LOG_ERR, "%s\n", "chdir");
        exit(EXIT_FAILURE);
    }

    /* 4to. Restablecemos el modo de archivo */
    umask(0);

    /* 5to. Cerramos los descriptores de archivos innecesarios */
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    /* Hacemos el verdadero trabajo */
    /**
     *En esta seccion lo que se hace es, otre otras cosas:
     *  -crear el socket servidor
     *  -ligar el socket a una direccion 
     *  -escuchar a la espera de clientes
     *  -acceptar las solicitudes de conexion
     * obs1:la manera en la que se controla que no exista mas de 'max_hilos' a la vez
     * es utilizando una variable global 'cant_hilos' que aumenta por cada hilo lanzado
     * y dismuniye por cada hilo finalizado, por supuesto, se utiliza un semaforo mutex
     * obs2:el vector de hilos se completa utilizando Round Robin
     * obs3:la obs1 no me convence del todo, aunque funciona. pero creo que seria mejor
     * utilizar senhales; por ejemplo, cuando un hilo termina le avisa a su padre.
     */
    	int sockfd;
	int sin_size;
	int i;
        struct sockaddr_in my_addr;
 	struct sockaddr_in their_addr; // información sobre la dirección del cliente
        //sigset_t conjunto_signal;
        /*struct sigaction action;
        action.sa_handler = signal_handler;
        sigaction(SIGCONT, &action, NULL);**/
        struct sigaction sa_reload;
        sa_reload.sa_handler = reload_handler;
        sigaction(SIGHUP, &sa_reload, NULL);
        signal(SIGCONT, signal_handler);
        //signal(SIGHUP, reload_handler);
        sin_size = sizeof(struct sockaddr_in);
        int yes =1;
        setjmp(val);

    open_fileconf(argv[1]);
    
    /*char logpath[60];
    strcpy(logpath, dc.path_log);
    strcat(logpath, dc.logfile);
    syslog(LOG_INFO, "%s\n", logpath);
    int logfd;//el log en el que se registraran los eventos
    if ((logfd = open(logpath, O_WRONLY | O_CREAT)) == -1){
        syslog(LOG_ERR, "%s\n", "error opening filelog");
        exit(EXIT_FAILURE);
    }*/
    if (bandera == 1){
            syslog(LOG_ERR, "%s\n", "fileconf releido");
            close(sockfd);
            for (i = 0; i < dc.max_hilos; i++){
                //pthread_cancel(hilos[i]);
                //close(socket_client[i]);
            }
    }
	pthread_t hilos[dc.max_hilos];
        int socket_client[dc.max_hilos];
        
 	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		syslog(LOG_ERR, "%s\n", "socket");
		exit(1);
	}
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
            syslog(LOG_ERR, "%s%s\n", "setsockopt: ",strerror(errno));
            exit(1);
        }
	my_addr.sin_family = AF_INET;// Ordenación de máquina
	my_addr.sin_port = htons(dc.num_puerto);	// short, Ordenación de la red
	my_addr.sin_addr.s_addr = INADDR_ANY;
	memset(&(my_addr.sin_zero), '\0', 8);
	if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))	== -1) {
                syslog(LOG_ERR, "%s%s\n", "bind: ",strerror(errno));
		exit(EXIT_FAILURE);
	}
	if (listen(sockfd, dc.backlog) == -1) {
		syslog(LOG_ERR, "%s\n", "listen");
		exit(1);
        }
        
        cant_hilos=0;
        bandera =0;
        i=0;
	while (1){
            syslog(LOG_INFO, "%s\n", "continua");
            if (cant_hilos < dc.max_hilos){
		if ((socket_client[i] = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
			syslog(LOG_ERR, "%s\n", "accept");
		}
		if (pthread_create(&hilos[i], NULL, (void *)interface_telnet, (void *)&socket_client[i]) != 0){
                    syslog(LOG_ERR, "%s\n", "pthread_create");
                    continue;
		}
		i = (i+1) % 10;
            }
            pause();
	}
        syslog(LOG_ERR, "%s\n", "fin");
	close(sockfd);
    closelog();
    exit(EXIT_SUCCESS);
}

void interface_telnet(int *sockfd){
   /*esta funcion inteface_telnet es la que se ejecutara por cada conexion aceptada
    * asi que debe encargarse de la autenticacion y de atender las solicitudes
    * del usuario como son ls,mkdir,rm.... para este ultimo caso, deber'a llamar a otra
    * funcion 'telnet' que debe ser implementada
    */
    
    if (pthread_mutex_lock(&mutex)!=0) {
            syslog(LOG_ERR, "%s\n", "mutex no se puede bloquear");
            return;
        }
        cant_hilos++;
        kill(getpid(), SIGCONT);
    if (pthread_mutex_unlock(&mutex)!=0) {
            syslog(LOG_ERR, "%s\n", "mutex no se puede desbloquear");
            return;
        }
    autenticar (sockfd);//se debe controlar que el usuario este registrado
    simpleBash_telnet(sockfd); //-->> aqui es donde se hace el verdadero trabajo
    if (pthread_mutex_lock(&mutex)!=0) {
            syslog(LOG_ERR, "%s\n", "mutex no se puede bloquear");
            return;
        }
        cant_hilos--;
        if (cant_hilos == dc.max_hilos - 1)
            kill(getpid(), SIGCONT);
    if (pthread_mutex_unlock(&mutex)!=0) {
            syslog(LOG_ERR, "%s\n", "mutex no se puede bloquear");
            return;
        }
    close(*sockfd);
}
int autenticar (int *sockfd){
    /**
     * 
     * @param sockfd -> descriptor del socket
     * @return  -1:si hubo algun error
     *           0:si el usuario esta registrado y se autentico correctamente
     *           1:si el usuario esta registrado pero no se autentico correctamente
     *           2:si el usuario no esta registrado
     * basicamente esta funcion debe verificar que el usuario esta registrado
     * en el archivo "<deamon>.users.acl", de ser asi, realizar la autenticacion.
     */
    int numbytes;
    char login[10], passwd[10];
    char *enviar;
    
    enviar = "Bienvenido!\n";
    if (send(*sockfd, enviar, strlen(enviar), 0) == -1)
        syslog(LOG_ERR, "%s%s\n", "send: ",strerror(errno));
    
    enviar = "login: ";
    if (send(*sockfd, enviar, strlen(enviar), 0) == -1)
        syslog(LOG_ERR, "%s%s\n", "send: ",strerror(errno));

    if ((numbytes=recv(*sockfd, login, BUFLEN, 0)) == -1) {
	syslog(LOG_ERR, "%s%s\n", "recv: ",strerror(errno));
	return -1;
    }
    syslog(LOG_INFO, "%s%s\n", "login: ",login);
    
    enviar = "password: ";
    if (send(*sockfd, enviar, strlen(enviar), 0) == -1)
        syslog(LOG_ERR, "%s%s\n", "send: ",strerror(errno));
    
    if ((numbytes=recv(*sockfd, passwd, BUFLEN, 0)) == -1) {
	syslog(LOG_ERR, "%s%s\n", "recv: ",strerror(errno));
	return -1;
        }
    
    syslog(LOG_INFO, "%s%s\n", "passwd: ",passwd);
    return 0;
}
/*
void telnet(int *sockfd){
   
    int numbytes, i, cmp, sw, cant;
    char command_line[100];
    char new_command_line[100];
    FILE *fp;
    char buf[BUFSZ];
    char *alias[] = {"alias copy=cp ",
                        "alias view=cat ",
                        "alias move=mv && ",
                        "alias delete=rm && "};
    while (1){
        sw = 0;
        new_command_line[0] = '\0';
        strcpy(buf, "$");
        if (send(*sockfd, buf, strlen(buf), 0) == -1){
                syslog(LOG_ERR, "%s%s\n", "send: ",strerror(errno));
                return;
        }
        memset(command_line, '\0', 100);
        if ((numbytes=recv(*sockfd, command_line, BUFLEN, 0)) == -1) {
            syslog(LOG_ERR, "%s%s\n", "recv: ",strerror(errno));
            return;
        }
        command_line[numbytes - 2] = '\0';
        syslog(LOG_INFO, "%s%s\n", "command_line recibido: ", command_line);
        
        if (buscar_comando(command_line, numbytes) == 0)
            syslog(LOG_INFO, "%s\n", "comando valido");
        else{
            strcpy(buf, "Comando invalido\n");
            if (send(*sockfd, buf, strlen(buf), 0) == -1)
                syslog(LOG_ERR, "%s%s\n", "send: ",strerror(errno));
            continue;
        }
        if (cmp = comparar_comando(command_line, "help", numbytes) == 0){
            strcpy(new_command_line, "cat /home/roque/NetBeansProjects/FTPLike2/listaComandos.txt");
            syslog(LOG_INFO, "%s\n", new_command_line);
            sw = 1;
        }
        if (cmp = comparar_comando(command_line, "close", numbytes) == 0){
                syslog(LOG_INFO, "%s\n", "close");
                return;
        }
        if (cmp = comparar_comando(command_line, "view", numbytes) == 0){
                strcat(new_command_line, alias[1]);
                strcat(new_command_line, command_line);
                syslog(LOG_INFO, "%s\n", new_command_line);
                sw = 1;
        }
        if (sw == 0){
            if((fp = popen(command_line, "r")) == NULL)
                syslog(LOG_ERR, "%s%s\n", "popen: ",strerror(errno));
        }else{
            if((fp = popen(new_command_line, "r")) == NULL)
                syslog(LOG_ERR, "%s%s\n", "popen: ",strerror(errno));
        }
            
        while((fgets(buf, BUFSZ, fp)) != NULL){
            if (send(*sockfd, buf, strlen(buf), 0) == -1)
                    syslog(LOG_ERR, "%s%s\n", "send: ",strerror(errno));
        }
    }
}  
int buscar_comando(char *s1, int size){
    int i;
    for (i = 0; i < 11; i++){
        if (comparar_comando(s1, array_command[i], size) == 0)
            return 0;
    }
    return 1;
}
int comparar_comando(char *s1, char *s2, int size){
    int i, cont = 0, j = 0;
    for (i = 0; i < size; i++){
        if (s1[i] != ' '){
            j = i;
            break;
        }
    }
    for (i = j; i < strlen(s2); i++){
        if (s1[i] != s2[cont]){
            return 1;
        }
        cont++;
    }
    if (s1[i] == ' ' || s1[i] == '\n') return 0;
}*/
void signal_handler (int valor){
    syslog(LOG_INFO, "%s\n", "demonio despierto");
}
void reload_handler(int valor){
    syslog(LOG_INFO, "%s\n", "releer fileconf");
    bandera = 1;
    longjmp(val, 1);
}
void open_fileconf(const char *path_fileconf){
    FILE *fileconf;
    if((fileconf = fopen(path_fileconf, "r")) == NULL) {
        syslog(LOG_ERR, "%s\n", "error opening fileconf");
        exit(EXIT_FAILURE);
    } else {
	  //fprintf (fileconf, "%d %d %d %s %s", 3490, 10, 10, "/var/log/","deamon.log");
	fscanf (fileconf, " %d %d %d %d %s %s ", &dc.num_puerto, &dc.max_hilos, 
                        &dc.timeout, &dc.backlog, dc.path_log, dc.logfile);
        if(fclose(fileconf) < 0) {
            syslog(LOG_ERR, "%s\n", "error closing fileconf");
            exit(EXIT_FAILURE);
        }
        syslog(LOG_INFO, "%d %d %d %d %s %s\n", dc.num_puerto,dc.max_hilos, 
                dc.timeout, dc.backlog, dc.path_log, dc.logfile);
    }
}

int enviar(char *mensaje,int *sockfd)
{
    if (send(*sockfd, mensaje, strlen(mensaje), 0) == -1)
    {
        syslog(LOG_ERR, "%s%s\n", "send: ",strerror(errno));
        return -1;
    }
    return 0;
}

int recibir(char *mensaje,int buflen,int *sockfd)
{
    int numbytes;
    if ((numbytes=recv(*sockfd, mensaje, buflen, 0)) == -1) 
    {
	syslog(LOG_ERR, "%s%s\n", "recv: ",strerror(errno));
	return -1;
    }
    return numbytes;
}